level: any
What is Eddie?
Eddie is a new code editor written in Liberty BASIC, for Liberty BASIC. Eddie can be customized and extended to meet the needs of the individual programmer. The newest version is available here: Eddie!
What is CodeAChrome?
CodeAChrome is a new, FREE code editing component. Some features of CodeAChrome.DLL:
The Bare Minimum CodeAChrome Program!
Because CodeAChrome comes with a built-in menu for file and editing functions, you need to make only two API calls to create a complete code editing program. To create the control, use CreateCodeAChrome. When the program ends, you must destroy the control with DestroyCodeAChrome. That's all you need! Here's a wee demo. The functions will be explained later in this article.
nomainwin
WindowWidth=580:WindowHeight=480
menu #1, "&File", "E&xit", [quit]
open "CodeAChrome Editor Demo" for window_nf as #1
#1 "trapclose [quit]"
open "CodeAChrome.dll" for dll as #r
hMain = hwnd(#1)
calldll #r, "CreateCodeAChrome", hMain as long,_
10 as long, 10 as long,550 as long, 400 as long, ret as long
if ret=0 then
notice "Error loading CodeAChrome. Program ended."
goto [quit]
end if
wait
[quit]
calldll #r, "DestroyCodeAChrome", ret as void
close #r
close #1
end
Eddie Uses CodeAChrome!

Getting Started with CodeAChrome
Since Eddie will now use CodeAChrome, we can remove the graphicbox for line numbers, the Liberty BASIC texteditor, and the timer command. The timer was used to check for a change in the origin so that the line numbers could be redrawn as needed. CodeAChrome has its own line number feature and texteditor, and it automatically keeps the line numbers in synch with the visible lines in the editor.
CodeAChrome.DLL allows only one CodeAChrome control in a Liberty BASIC program.
To add CodeAChrome, we need the handle of the window, which we obtain with the HWND command. We also must open the DLL and give it a handle. Liberty BASIC lets us call on Windows DLLs like #user32 without opening them, but we must explicitly open other DLLs.
open "CodeAChrome.dll" for dll as #r
hMain = hwnd(#1) 'handle of program window
Next, we create the control with CreateCodeAChrome. We'll place it at the left edge of our window, so the left X argument is set to 0. We want to leave room for the buttons and the branch label combobox, so the top Y location is set to 52. The width and the height don't really matter, because we'll activate the resizehandler and resize the control as soon as the window opens. If the function returns 0, the creation of the CodeAChrome control failed and we end the program.
calldll #r, "CreateCodeAChrome",_ 'code editing component
hMain as long,_ 'handle of program window
0 as long,_ 'x location of control
52 as long,_ 'y location of control
545 as long,_ 'width of control
250 as long,_ 'height of control
ret as long 'nonzero=success
if ret=0 then
notice "Error loading CodeAChrome. Program ended."
goto [quit]
end if
Moving and Resizing CodeAChrome
The window will have a sizing frame so that the user can resize it. When we used a Liberty BASIC texteditor and graphicbox, we issued commands that caused them to resize automatically when a window was resized. CodeAChrome does not automatically resize, so we'll set up a handler for the event of resizing:
#1 "resizehandler [resize]"
For more info on the resizehandler, see Tip Corner.
To move and resize CodeAChrome, use the MoveCodeAChrome function. The arguments required are the new X and Y location and the desired width and height of the control. The function does not return a value.
[resize]
ww=WindowWidth : wh=WindowHeight-52
calldll #r, "MoveCodeAChrome",_ 'move/resize the control
0 as long,_ 'new x location
52 as long,_ 'new y location
ww as long,_ 'new width
wh as long,_ 'new height
re as void
#1 "refresh"
wait
As you can see in the code above, we use the WindowWidth and WindowHeight to calculate the new size for CodeAChrome. These variables are set by Liberty BASIC to contain the client area width and height when the resizehandler is activated. If we activate the resizehandler we don't need to do any extra API calls to get the dimensions of the client area in order to set the size for CodeAChrome. The size of the client area varies from computer to computer, dependent upon the user's display settings.
We can force the resizehandler to be activated right after the window opens by using the MoveWindow API call. For more on this API call, see API Corner. Be sure to use slightly different values than the original WindowWidth and WindowHeight for the new width and height of the window, so that LB recognizes that the window has been resized.
'activate resizehandler
calldll #user32, "MoveWindow",_
hMain as ulong, _ 'window handle
UpperLeftX as long,_ 'x location of window
UpperLeftY as long,_ 'y location of window
730 as long,_ 'desired width of window
590 as long,_ 'desired height of window
1 as boolean, ret as long
Destroying CodeAChrome
It's important to destroy the control before we close the program window. We simply add DestroyCodeAChrome to the [quit] subroutine. The function does not return a value. We must be sure to close the DLL, as well.
[quit]
gosub [writeIniFile]
calldll #r, "DestroyCodeAChrome", ret as void
close #r : close #1 : END
Editing Functions
CodeAChrome has a built-in context menu that includes all of the standard editing functions, plus a find/replace dialog. It is accessed when the user right-clicks on the CodeAChrome control.
Liberty BASIC provides an automatic Edit Menu for the texteditor control. We no longer have that feature in Eddie, so if we want an Edit Menu on the menubar, we must implement it ourselves.
Menu #1, "&Edit", "&Undo",[undo],"Cu&t",[cut],"&Copy",[copy],"&Paste",[paste],|,_
"&Select All",[selectall]
CodeAChrome provides several editing functions, which are included in the code below. These functions to not return a value. They are written on two lines for clarity in Eddie, but they can be placed on a single line for conciseness. The editing functions all look like this one:
calldll #r, "EditUndo", re as void
The functions are EditUndo, EditCutText, EditCopyText, EditPasteText, EditSelectAll. As they appear in Eddie:
[undo]
calldll #r, "EditUndo",_ 'undo last operation
re as void
wait
[cut]
calldll #r, "EditCutText",_ 'cut selected text to clipboard
re as void
wait
[copy]
calldll #r, "EditCopyText",_ 'copy selected text to clipboard
re as void
wait
[paste]
calldll #r, "EditPasteText",_ 'paste contents of clipboard at cursor
re as void
wait
[selectall]
calldll #r, "EditSelectAll",_ 'select all text in control
re as void
wait
File Functions - New, Open, Save, Print
These functions are included in the automatic right-click context menu, but we can also access them directly. None of the functions returns a value. To remove all contents of the CodeAChrome control, issue a DoNew command:
calldll #r, "DoNew",_ 'clear control
re as void
To load a file from disk into the CodeAChrome and replace the current contents of the control, issue a FileLoad command. It requires the name of the file on disk, and it does not return a value:
calldll #r, "FileLoad",_ 'load file into codeachrome
openfile$ as ptr,_ 'name of file
re as void 'no return
To save the contents of the CodeAChrome control to disk, use the FileSave function. It requires the desired filename, and it does not return a value.
calldll #r, "FileSave",_ 'save contents to disk
file$ as ptr,_ 'save filename
re as void
When Eddit attempts to run the current code in Liberty BASIC, it writes the contents of the control to disk with the name "tempfile$". It then uses the RUN command to run tempfile$.
calldll #r, "FileSave",_ 'save contents to disk
tempfile$ as ptr,_ 'save filename
re as void
RUN LibertyExe$ + " " + chr$(34) + tempfile$ + chr$(34)
CodeAChrome makes it very easy to print a hard copy of the contents. Use the DoPrint command, which does not return a value.
calldll #r, "DoPrint",_ 'print contents of control
re as void
Scrolling a Line to the Top
Eddie has a tool that allows a user to specify a line number and scroll that line to the top. To convert to CodeAChrome, keep in mind that the line numbers begin at 0.
We'll first need to retrieve the number of lines in the control with GetNumberOfLines. We won't try to scroll to a line number that is larger than the actual number of lines contained in the control. We then call SetTopLine to scroll the designated line to the top. This is similar to the Liberty BASIC "!origin" command, but it only scrolls the lines of text, not the columns. SetTopLine does not return a value.
[gotoLine]
calldll #r, "GetNumberOfLines",_
rowcount as long 'returns number of lines, 0-indexed
gotoLine=rowcount-1
msg$="Go to line? Max is ";gotoLine
prompt msg$;gotoLine
if gotoLine>rowcount-1 then gotoLine=rowcount
if gotoLine<0 then gotoLine=0
calldll #r, "SetTopLine",_ 'scroll this line to top
gotoLine as long,_ 'index of line
re as void 'no return
wait
Filling the Branch Label Combobox
CodeAChrome has functions to retrieve the length of a line of text, and the text in a line. We'll use those instead of the texteditor "!line" command that we used in previous versions of Eddie. After we retrieve the number of lines, we'll iterate through them in a loop. For each line, we'll retrieve the length of the text so that we can set up the proper size string buffer to hold the text. To do that, we use the GetLineTextLength function, like this:
calldll #r, "GetLineTextLength",_ 'retrieve length of text in line
i as long,_ 'index of line
length as long 'returns length
We then create a string variable large enough to hold the text.
line$ = space$(length + 1) 'create buffer to receive text
Then we use GetLineText to get the text into the "line$" variable. This function does not return a value.
calldll #r, "GetLineText",_
i as long,_ 'index of line to retrieve
line$ as ptr,_ 'buffer for text
ret as void
After the function returns, the text in the line is in the variable called "line$". We then treat it just as we did in previous versions of Eddie, checking to see if the line contains a branch label, sub, or function definition. If it does, we load it into the combobox, along with the index of the line. Here is the routine that fills the branch label combobox:
[fillBranch]
calldll #r, "GetNumberOfLines",_
rowcount as long 'returns number of lines, 0-indexed
rowcount = rowcount - 1 'because lines begin at 0
REDIM branch$(rowcount)
branch$(1)="" + space$(300) + "1"
'fill combobox with labels, functions and sub definitions
'pad with 300 spaces, then add line number
bx=2
For i=0 to rowcount
calldll #r, "GetLineTextLength",_ 'retrieve length of text in line
i as long,_ 'index of line
length as long 'returns length
line$ = space$(length + 1) 'create buffer to receive text
calldll #r, "GetLineText",_
i as long,_ 'index of line to retrieve
line$ as ptr,_ 'buffer for text
ret as void
If Left$(Trim$(line$),1)="[" Then
branch$(bx)=Trim$(line$) + space$(300) + str$(i)
bx=bx+1
End If
If Lower$(Word$(line$,1))="function" Then
branch$(bx)=Trim$(line$) + space$(300) + str$(i)
bx=bx+1
End If
If Lower$(Word$(line$,1))="sub" Then
branch$(bx)=Trim$(line$) + space$(300) + str$(i)
bx=bx+1
End If
Next
#1.c "reload"
#1.c "selectindex 1";
return
Scrolling to a Branch Label in the Code
When the user selects a branch label from the combobox, we check the last part of the selected item to extract the line number. (See the previous Eddie installment for more information on this techinique.) We then use the SetTopLine function described earlier in this article to scroll the correct line to the top.
[chooseBranch]
'** CHOOSE BRANCH, SET EDITOR TO THAT POSITION
#1.c "selection? branchselect$"
'get the text at the end of the item
ln$ = right$(branchselect$, 6)
'get value of text, which will = linenumber
linenumber = val(ln$)
'now scroll texteditor so that line is at top
calldll #r, "SetTopLine",_ 'scroll this line to top
linenumber as long,_ 'index of line
re as void 'no return
Wait
There's More!
Watch for more features of CodeAChrome in future versions of Eddie.